package pira.db.ui;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.neo4j.graphdb.Direction;
import org.neo4j.graphdb.Node;
import org.neo4j.graphdb.Relationship;
import org.neo4j.graphdb.ReturnableEvaluator;
import org.neo4j.graphdb.StopEvaluator;
import org.neo4j.graphdb.Transaction;
import org.neo4j.graphdb.Traverser;
import org.neo4j.graphdb.Traverser.Order;
import org.neo4j.graphdb.index.Index;
import org.neo4j.graphdb.index.IndexHits;
import org.neo4j.graphdb.index.RelationshipIndex;

import pira.elements.Author;
import pira.elements.Paper;
import pira.parser.xml.dblp.XMLFilesReader;
import pira.parser.xml.dblp.XMLFilesReader.MyRelationshipTypes;
import pira.utils.Log;

public class GraphDBUI extends Neo4jDBUI {
	
	// configuration for database
	// default database path
	public static final String DEFAULTDATABASE = "../SampleDB1.3.3/my-neo4j-db/";
	// public static final String DEFAULTDATABASE = "sample-db/";

	// whole node number
	public static final long ALLNODE = 1659962;
	
	// the external node
	public long EXTERNALNODEID = ALLNODE + 1;
	
	// configuration file
	public static final String CONFIG = "neo4j_config.props";
	
	// the range for the calculation of citation number of papers
	public static final long BEGINPAPERID = 0;	//0;
	public static final long ENDPAPERID = 62650;		//737636;

	public Index<Node> papers;
	public Index<Node> authors;

	public RelationshipIndex writes;
	public RelationshipIndex writtenBys;
	public RelationshipIndex cites;
	public RelationshipIndex citedBys;
	public RelationshipIndex personCites;
	public RelationshipIndex personCitedBys;
	public RelationshipIndex coauthors;
	
	public GraphDBUI() {
		super(DEFAULTDATABASE, CONFIG);
		tx = graphDb.beginTx();
		try {
			this.papers = index.forNodes("papers");
			this.authors = index.forNodes("authors");
			this.writes = index.forRelationships("writes");
			this.writtenBys = index.forRelationships("written_bys");
			this.cites = index.forRelationships("cites");
			this.citedBys = index.forRelationships("citedBys");
			this.personCites = index.forRelationships("personCites");
			this.personCitedBys = index.forRelationships("personCitedBys");
			this.coauthors = index.forRelationships("coauthors");
		tx.success();
		} finally {
			tx.finish();
		}
	}
	
	public Node getVirtualNode() {
		return graphDb.getNodeById(EXTERNALNODEID);
	}

	public void calculHIndex() {
		long startTime = System.currentTimeMillis();
		Log hLog = new Log("Infos_Of_Author.txt");
		String str = "Order\tAuthorID\tCitation Number\t" +
				"Publication Number\tH-Index\tAuthorName\n";
		hLog.write(str);
		System.out.print(str);
		
		long authorID = BEGINAUTHORID;
		tx = graphDb.beginTx();
		try {
			int authorCount = 0;

			
			while (authorID < ENDAUTHORID) {
				Node author = graphDb.getNodeById(authorID);
				if (author.hasProperty("full name")) {
					authorCount++;
					str = authorCount + "\t" + authorID + "\t";
					System.out.print(str);
					hLog.write(str);
					
					calculateAuthorHIndex(author, authorID, hLog);
				}
				authorID++;
			}
			str = "In total, there're " + authorCount + " authors.\n";
			System.out.print(str);
			hLog.write(str);
			tx.success();
		} finally {
			hLog.close();
			tx.finish();
		}
		
		long timePassed = System.currentTimeMillis() - startTime;
		System.out.println("Run " + timePassed/1000 + " seconds.");
	}
	
	private void calculateAuthorHIndex(Node author, long authorID, Log hLog) {
		
		// algorithm for calculate H-Index
		String str = null;

		int publicationNumber = Integer.parseInt((String) author.getProperty("publication number"));
		Iterator<Relationship> rels = author.getRelationships(
				XMLFilesReader.MyRelationshipTypes.WRITE).iterator();
		int i = 1;
		for (i= 1; i <= publicationNumber; i++) {
			int count = 0;
			while (rels.hasNext()) {
				Node paper = rels.next().getEndNode();

				if (!paper.hasProperty("find"))
					continue;
				
				int size = 0;
				try {
					size = Integer.parseInt((String) paper.getProperty("cite times"));
				} catch (ClassCastException e) {
					size = (Integer) paper.getProperty("cite times");
				}
				if (size >= i)
					count++;
				if (count >= i)
					break;
			}

			if (count < i)
				break;
		}

		author.setProperty("h_index", "" + (i - 1));
		
		str = (String) author.getProperty("citation number") + "\t" 
			+ (String) author.getProperty("publication number") + "\t"
			+ (String) author.getProperty("h_index") +"\t"
			+ (String) author.getProperty("full name") + "\n";
		System.out.print(str);
		hLog.write(str);
	}
	
	/*
	 * This method calculate the number of citation and the number of publication
	 * 		for a author and update information to database
	 * Log files used: Citation_number_for_authors.txt
	 */

	public void calculateCitaNumAndPubliNumOfAuthor() {
		Log log = new Log("Citation_number_for_authors.txt");
		tx = graphDb.beginTx();
		try {
			// record how many authors has been traversed
			long authorID = BEGINAUTHORID;
			int authorCount = 0;
			while (authorID < ENDAUTHORID) {
				Node author = graphDb.getNodeById(authorID);
				if (author.hasProperty("full name")) {
					authorCount++;

					int citationNumber = 0;
					int publicationNumber = 0;
					String str = null;
					String name = (String) author.getProperty("full name");
					str = "The " + authorCount + " th author : " + name
							+ "\tID: " + authorID + "\n";
					System.out.print(str);
					log.write(str);

					// get all the papers that he writes
					Iterator<Relationship> wrs = author.getRelationships(
							XMLFilesReader.MyRelationshipTypes.WRITE,
							Direction.OUTGOING).iterator();

					while (wrs.hasNext()) {
						
						publicationNumber++;
						// get the paper
						Node p = wrs.next().getEndNode();
						str = p.getId() + " : "
								+ (String) p.getProperty("title") + "\n";
						System.out.print(str);
						log.write(str);

						// get all the papers that cite this paper
						Iterator<Relationship> cs = p.getRelationships(
								XMLFilesReader.MyRelationshipTypes.CITE,
								Direction.INCOMING).iterator();

						if (cs.hasNext()) {
							str = "=========> paper who cite this: \n";
							System.out.print(str);
							log.write(str);
						}

						int i = 1;
						while (cs.hasNext()) {
							Relationship rel = cs.next();

							// get the cite paper
							Node cp = rel.getStartNode();
							String t = (String) cp.getProperty("title");

							// get the weight
							int weight = 0;
							try {
								weight = Integer.parseInt((String) rel
									.getProperty("weight"));
							} catch (ClassCastException e) {
								weight = (Integer) rel.getProperty("weight");
							}
							str = i + " : " + t + "\n";
							System.out.print(str);
							log.write(str);
							i++;
							citationNumber += weight;
						}

					}

					str = "Citation Number for the author is : "
							+ citationNumber + "\n";
					System.out.print(str);
					log.write(str);
					
					str = "Publication Number for the author is :" 
							+ publicationNumber + "\n\n";
					System.out.print(str);
					log.write(str);
					
					author.setProperty("citation number", "" + citationNumber);
					author.setProperty("publication number", "" + publicationNumber);
				}
				authorID++;
			}
			tx.success();
		} finally {
			log.close();
			tx.finish();
		}
	}

	/*
	 * This method is used to calculate cited times of every paper
	 * It uses the log file: Cited_times_of_papers.txt
	 */
	
	public void calculCitedTimesForAllPapers() {
		long nodeID = BEGINPAPERID;
		tx = graphDb.beginTx();
		Log log = new Log("Cited_times_of_papers.txt");
		String str = null;
		
		str = "PaperID\tCitationNumber\tTitle\n";
		System.out.print(str);
		log.write(str);

		try {
			while (nodeID < ENDPAPERID) {
				Node paper = graphDb.getNodeById(nodeID);
				if (paper.hasProperty("title")) {
					String title = (String) paper.getProperty("title");
					int citedTimes = calculCitedTimesOfPaper(paper);
					
					str= nodeID + "\t" + citedTimes + "\t" + title + "\n"; 
					System.out.print(str);
					log.write(str);
				}
				nodeID++;
			}
			tx.success();
		} finally {
			log.close();
			tx.finish();
		}
	}
	
	private int calculCitedTimesOfPaper(Node paper) {
		Iterator<Relationship> rels = paper
				.getRelationships(
						XMLFilesReader.MyRelationshipTypes.CITE,
						Direction.INCOMING).iterator();
		int count = 0;
		int iCount = 0;
		int eCount = 0;
		while (rels.hasNext()) {
			Relationship c = rels.next();
			Node start = c.getStartNode();
			int weight= 0;
			
			try {
				weight += Integer.parseInt((String) c
						.getProperty("weight"));
			} catch (ClassCastException e) {
				weight += (Integer) c.getProperty("weight");
			}
			
			count += weight;
			if (start.getId() == EXTERNALNODEID)
				eCount += weight;
			else
				iCount += weight;
		}
		
		paper.setProperty("cite times", "" + count);
		paper.setProperty("internal cited times", iCount);
		paper.setProperty("external cited times", eCount);
		
		return count;
	}
	
	public void lookForPersonCitationDetail(long NodeId) {
		tx = graphDb.beginTx();
		try {
			Node a = graphDb.getNodeById(NodeId);
			Iterator<Relationship> rels = a.getRelationships(
					MyRelationshipTypes.PERSON_CITE, Direction.INCOMING)
					.iterator();

			System.out.println("\n================================");
			System.out.println("Info for who PERSONCITE me : " + NodeId);

			while (rels.hasNext()) {
				Relationship r = rels.next();
				Node start = r.getStartNode();
				System.out.println("(Me) <-- PERSONCITE -- (" + start.getId()
						+ "," + (String) start.getProperty("full name") + ").");
				System.out.println("\t" + r.getProperty("index"));
			}

			rels = a.getRelationships(MyRelationshipTypes.PERSON_CITE,
					Direction.OUTGOING).iterator();

			System.out.println("\n================================");
			System.out.println("Info for who I PERSONCITE : " + NodeId);

			while (rels.hasNext()) {
				Relationship r = rels.next();
				Node end = r.getEndNode();
				System.out.println("(Me) -- PERSONCITE --> (" + end.getId()
						+ "," + (String) end.getProperty("full name") + ").");
				System.out.println("\t" + r.getProperty("index"));
			}
			tx.success();
		} finally {
			tx.finish();
		}
	}

	public void lookForCitationDetail(long NodeId) {
		tx = graphDb.beginTx();
		try {
			Node p = graphDb.getNodeById(NodeId);
			Iterator<Relationship> rels = p.getRelationships(
					XMLFilesReader.MyRelationshipTypes.CITE, Direction.INCOMING)
					.iterator();

			System.out.println("================================");
			System.out.println("Info for Node : " + NodeId);

			while (rels.hasNext()) {
				Relationship r = rels.next();
				Node start = r.getStartNode();
				System.out.println("(Me) <-- Cite -- (" + start.getId() + ","
						+ (String) start.getProperty("title") + ").");
				System.out.println("\tWeight is : "
						+ (String) r.getProperty("weight"));
			}
			tx.success();
		} finally {
			tx.finish();
		}
	}

	public int addCitation(Node myNode, String title, String[] names) {
		// my title is cited by titles
		int result = -1;
		String str = null;
		String myTitle = (String) myNode.getProperty("title");
		Log citation = new Log("Add_citation.txt", true);

		str = "Add citation From (" + myNode.getId() + "," + myTitle + ") to ("
				+ title + ")\n";
		System.out.print(str);
		citation.write(str);

		Transaction tx = graphDb.beginTx();
		IndexHits<Node> hits, anotherHits;
		String another = title + ".";
		try {
			hits = papers.get("paperID", title.hashCode());
			anotherHits = papers.get("paperID", another.hashCode());

			boolean found = false;
			Node node = null;
			if (hits.size() != 0 || anotherHits.size() != 0) {
				/*
				 * if find, compare authors
				 */
				IndexHits<Node> goal = null;
				String[] authors = null;
				if (hits.size() != 0)
					goal = hits;
				else {
					goal = anotherHits;
					title = another;
				}
				// if there's at least one author match, we trait them as the
				// same
				lable: for (Node p : goal) {
					authors = (String[]) p.getProperty("authors");
					for (String name : names)
						for (String author : authors)
							if (name.equals(author)) {
								found = true;
								node = p;
								break lable;
							}
				}
			}

			if (found) {
				str = "\tAdd citation from " + myNode.getId() + " to ("
						+ node.getId() + "," + title + ").\n\n";
				System.out.print(str);
				citation.write(str);
				// add citation between two nodes
				addCitationBetween2Nodes(myNode, myTitle, node, title);
				result = 1;
			} else {
				str = "\tAdd citation from " + myNode.getId()
						+ " to EXTERNAL NODE.\n\n";
				System.out.print(str);
				citation.write(str);
				// should add the citation to external node here
				addCitationToExternalNode(myNode, myTitle);
				result = 2;
			}
			tx.success();
		} finally {
			citation.close();
			tx.finish();
		}
		return result;
	}

	private Traverser getAuthorsTraverser(final Node paper) {
		return paper.traverse(Order.BREADTH_FIRST, StopEvaluator.DEPTH_ONE,
				ReturnableEvaluator.ALL_BUT_START_NODE,
				XMLFilesReader.MyRelationshipTypes.WRITE, Direction.INCOMING);
	}


	/*
	 * Delete all the citation relationships for paper
	 * only delete the incoming citation
	 */
	public void deleteCitationForNode(long id) {
		Node myNode = graphDb.getNodeById(id);
		Transaction tx = graphDb.beginTx();
		try {
			Iterator<Relationship> rels = myNode.getRelationships(
					XMLFilesReader.MyRelationshipTypes.CITE, Direction.INCOMING)
					.iterator();
			while (rels.hasNext()) {
				Relationship rel = rels.next();
				cites.remove(rel);
				rel.delete();
			}

			rels = myNode.getRelationships(
					XMLFilesReader.MyRelationshipTypes.CITED_BY,
					Direction.OUTGOING).iterator();
			while (rels.hasNext()) {
				Relationship rel = rels.next();
				citedBys.remove(rel);
				rel.delete();
			}
			
			myNode.setProperty("cite times", "" + 0);
			myNode.setProperty("internal cited times", "" + 0);
			myNode.setProperty("external cited times", "" + 0);
			
			Relationship externalCite = cites.get("index", 
					EXTERNAL + "_" + (String) myNode.getProperty("title")).getSingle();
			cites.remove(externalCite);
			externalCite.delete();
			
			Relationship externalCited = citedBys.get("index", 
					(String) myNode.getProperty("title") + "_" + EXTERNAL).getSingle();
			citedBys.remove(externalCited);
			externalCited.delete();
		} finally {
			tx.success();
			tx.finish();
		}
	}

	/*
	 * Add the citation between two nodes in database
	 */
	private void addCitationBetween2Nodes(Node myNode, String myTitle,
			Node node, String title) {
		System.out.println("Cite Paper found in DB!");
		String value = null;
		value = myTitle + "_" + title;
		Relationship cited = citedBys.get("index", value).getSingle();

		if (cited == null) {
			cited = myNode.createRelationshipTo(node,
					MyRelationshipTypes.CITED_BY);
			Relationship cite = node.createRelationshipTo(myNode,
					MyRelationshipTypes.CITE);

			// set the property and add to index
			cited.setProperty("weight", "" + 1);
			cited.setProperty("index", value);
			citedBys.add(cited, "index", value);
			System.out.println("run here!");
			System.out.println("index + " + value);

			value = title + "_" + myTitle;
			cite.setProperty("weight", "" + 1);
			cite.setProperty("index", value);
			cites.add(cite, "index", value);
			System.out.println("add to index.");
			System.out.println("index + " + value);

			// author citations
			Traverser authorTraverser = this.getAuthorsTraverser(myNode);
			Traverser citeAuthorTraverser = this.getAuthorsTraverser(node);

			for (Node myAuthor : authorTraverser) {
				String myAuthorName = (String) myAuthor
						.getProperty("full name");
				for (Node citeAuthor : citeAuthorTraverser) {
					String citeAuthorName = (String) citeAuthor
							.getProperty("full name");

					Relationship personCited = personCitedBys.get(
							"index",
							myAuthorName + "_" + myTitle + "_" + title + "_"
									+ citeAuthorName).getSingle();

					// if citation does not exist
					if (personCited == null) {
						personCited = myAuthor.createRelationshipTo(citeAuthor,
								MyRelationshipTypes.PERSON_CITED_BY);
						Relationship personCite = citeAuthor
								.createRelationshipTo(myAuthor,
										MyRelationshipTypes.PERSON_CITE);

						value = myAuthorName + "_" + myTitle + "_" + title
								+ "_" + citeAuthorName;
						personCited.setProperty("weight", "" + 1);
						personCited.setProperty("index", value);
						personCitedBys.add(personCited, "index", value);

						value = citeAuthorName + "_" + title + "_" + myTitle
								+ "_" + myAuthorName;
						personCite.setProperty("weight", "" + 1);
						personCite.setProperty("index", value);
						personCites.add(personCite, "index", value);
					} else {
						System.out.println("Person Citation already exist!");
					}
				}
			}
		} else {
			System.out.println("Citation Exist!");
			// System.exit(1);
		}

	}

	/*
	 * Add citation between a node and the external node
	 */
	private void addCitationToExternalNode(Node myNode, String myTitle) {
		System.out.println("Cite paper NOT found in DB!");
		Node virtualNode = getVirtualNode();

		Relationship externalCite = cites
				.get("index", EXTERNAL + "_" + myTitle).getSingle();

		// if the citation doesn't exist
		if (externalCite == null) {
			System.err.println("Not exist the citation to external node!");

			Relationship cited = myNode.createRelationshipTo(virtualNode,
					XMLFilesReader.MyRelationshipTypes.CITED_BY);
			externalCite = virtualNode.createRelationshipTo(myNode,
					XMLFilesReader.MyRelationshipTypes.CITE);

			cited.setProperty("weight", "" + 1);
			externalCite.setProperty("weight", "" + 1);
			citedBys.add(cited, "index", myTitle + "_" + EXTERNAL);
			cites.add(externalCite, "index", EXTERNAL + "_" + myTitle);
			System.out.println("---------weight :" + 1 + "\n");
		} else {
			System.out.print("Already exist citation to external node!");
			int weight = Integer.parseInt((String) externalCite
					.getProperty("weight"));

			// update the weight property
			cites.remove(externalCite, "index", EXTERNAL + "_" + myTitle);
			externalCite.setProperty("weight", "" + (weight + 1));
			cites.add(externalCite, "index", EXTERNAL + "_" + myTitle);

			Relationship externalCited = citedBys.get("index",
					myTitle + "_" + EXTERNAL).getSingle();
			citedBys.remove(externalCited, "index", myTitle + "_" + EXTERNAL);
			externalCited.setProperty("weight", "" + (weight + 1));
			citedBys.add(externalCited, "index", myTitle + "_" + EXTERNAL);
			System.out.println("---------weight :" + (weight + 1) + "\n");
		}
	}

	/*
	 * Add a Java object Author to Database
	 */
	public long addAuthorToDB(Author author) {
		tx = graphDb.beginTx();
		try {
			Node node = graphDb.createNode();

			// how to distinct two authors?
			// node.setProperty("authorID", author);
			node.setProperty("full name", author.getFullName());

			if (author.getFirstName() != null)
				node.setProperty("first name", author.getFirstName());
			else
				node.setProperty("first name", NOT_EXIST);

			if (author.getMiddleName() != null)
				node.setProperty("middle name", author.getMiddleName());
			else
				node.setProperty("middle name", NOT_EXIST);

			if (author.getLastName() != null)
				node.setProperty("last name", author.getLastName());
			else
				node.setProperty("last name", NOT_EXIST);

			authors.add(node, "authorID", author.getFullName().hashCode());
			return node.getId();
		} finally {
			tx.success();
			tx.finish();
		}
	}

	/*
	 * Add the object Paper to database
	 */
	public long addPaperToDB(Paper paper) {
		tx = graphDb.beginTx();
		try {
			Node node = graphDb.createNode();

			node.setProperty("paperID", paper.getTitle().hashCode());
			if (paper.type != null)
				node.setProperty("type", paper.type.toString());
			else
				node.setProperty("type", NOT_EXIST);

			if (paper.key != null)
				node.setProperty("key", paper.key);
			else
				node.setProperty("key", NOT_EXIST);

			String[] names = new String[paper.getAuthors().size()];
			if (paper.getAuthors().size() != 0) {
				for (int i = 0; i < paper.getAuthors().size(); i++) {
					names[i] = paper.getAuthors().get(i).toString();
					System.out.println(names[i]);

					// add the relationship of write/writtenBy
					IndexHits<Node> hits = authors.get("authorID",
							names[i].hashCode());
					Node authorNode = hits.getSingle();

					Relationship write = authorNode.createRelationshipTo(node,
							MyRelationshipTypes.WRITE);
					Relationship writtenBy = node.createRelationshipTo(
							authorNode, MyRelationshipTypes.WRITTEN_BY);
					write.setProperty("title", paper.getTitle());
					writtenBy.setProperty("title", paper.getTitle());
					writes.add(write, "index", names[i] + "_" + paper.getTitle());
					writtenBys.add(writtenBy, "index", paper.getTitle() + "_"
							+ names[i]);
				}

				// add the relationship of co-author
				if (paper.getAuthors().size() > 1) {
					for (int i = 0; i < paper.getAuthors().size(); i++) {
						Node node1 = authors.get("authorID",
								names[i].hashCode()).getSingle();
						for (int j = 0; j < paper.getAuthors().size(); j++) {
							if (i != j) {
								Node node2 = authors.get("authorID",
										names[j].hashCode()).getSingle();
								Relationship coauthor1 = node1
										.createRelationshipTo(node2,
												MyRelationshipTypes.CO);
								Relationship coauthor2 = node2
										.createRelationshipTo(node1,
												MyRelationshipTypes.CO);
								coauthor1.setProperty("paper", paper.getTitle());
								coauthor2.setProperty("paper", paper.getTitle());
								coauthors.add(coauthor1, "index", names[i]
										+ "_" + names[j]);
								coauthors.add(coauthor2, "index", names[j]
										+ "_" + names[i]);
							}
						}
					}
				}
			}
			node.setProperty("authors", names);

			if (paper.editor != null)
				node.setProperty("editor", paper.editor);
			else
				node.setProperty("editor", NOT_EXIST);

			if (paper.getTitle() != null)
				node.setProperty("title", paper.getTitle());
			else
				node.setProperty("title", NOT_EXIST);

			if (paper.publisher != null)
				node.setProperty("publisher", paper.publisher);
			else
				node.setProperty("publisher", NOT_EXIST);

			if (paper.mdate != null) {
				DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
				node.setProperty("publish date", df.format(paper.mdate));
			} else
				node.setProperty("publish date", NOT_EXIST);

			if (paper.bookTitle != null)
				node.setProperty("book title", paper.bookTitle);
			else
				node.setProperty("book title", NOT_EXIST);

			if (paper.cdrom != null)
				node.setProperty("cdrom", paper.cdrom);
			else
				node.setProperty("cdrom", NOT_EXIST);

			if (paper.pages != null)
				node.setProperty("pages", paper.pages);
			else
				node.setProperty("pages", NOT_EXIST);

			if (paper.paperURL != null)
				node.setProperty("paper URL", paper.paperURL);
			else
				node.setProperty("paper URL", NOT_EXIST);

			if (paper.isbn != null)
				node.setProperty("isbn", paper.isbn);
			else
				node.setProperty("isbn", NOT_EXIST);

			if (paper.citedTimes != -1)
				node.setProperty("cite times", paper.citedTimes);
			else
				node.setProperty("cite times", -1);

			if (paper.cite != null)
				node.setProperty("cite", paper.cite);
			else
				node.setProperty("cite", NOT_EXIST);

			if (paper.series != null)
				node.setProperty("series", paper.series);
			else
				node.setProperty("series", NOT_EXIST);

			if (paper.ee != null)
				node.setProperty("ee", paper.ee);
			else
				node.setProperty("ee", NOT_EXIST);

			if (paper.crossref != null)
				node.setProperty("crossref", paper.crossref);
			else
				node.setProperty("crossref", NOT_EXIST);

			papers.add(node, "paperID", paper.getTitle().hashCode());
			return node.getId();
		} finally {
			tx.success();
			tx.finish();
		}
	}

	// add lien between papers
	public void addLien(Paper currP, Paper lastP) {
		tx = graphDb.beginTx();
		try {
			IndexHits<Node> hit1 = papers
					.get("paperID", currP.getTitle().hashCode());
			IndexHits<Node> hit2 = papers
					.get("paperID", lastP.getTitle().hashCode());
			Node n1 = hit1.getSingle();
			Node n2 = hit2.getSingle();

			System.out.println("Create lien!");
			n1.createRelationshipTo(n2, MyRelationshipTypes.LIEN);
		} finally {
			tx.success();
			tx.finish();
		}
	}

	/*
	 * try to find a author in database, if found, return all the authors that satisfy
	 */
	public Vector<Author> findAuthor(Author author) {
		Vector<Author> result = null;
		String fullName = author.getFullName();
		IndexHits<Node> hits = findAuthorInDB(fullName);
		if (hits.size() != 0) {
			System.out.println("Author Found in Database!");
			result = new Vector<Author>();
			for (Node node : hits) {
				result.add(new Author(node));
			}
		} else {
			System.out.println("Author not exist!");
		}
		return result;
	}
	
	public IndexHits<Node> findAuthorInDB(String fullName) {
		IndexHits<Node> hits = null;
		tx = graphDb.beginTx();
		try {
			hits = authors.get(Author.AUTHORID, fullName.hashCode());
			tx.success();
		} finally {
			tx.finish();
		}
		return hits;
	}

	
	/*
	 * try to find a paper in database
	 */
	public Vector<Paper> findPaper(Paper paper) {
		Vector<Paper> result = null;
		String title = paper.getTitle();
		IndexHits<Node> hits = findPaperInDB(title);
		if (hits.size() != 0) {
			System.out.println("Paper Found in Database!");
			result = new Vector<Paper>();
			for (Node node : hits) {
				result.add(new Paper(node));
			}
		} else {
			System.out.println("Paper not exist!");
		}
		return result;
	}

	public IndexHits<Node> findPaperInDB(String title) {
		IndexHits<Node> hits = null;
		tx = graphDb.beginTx();
		try {
			hits = papers.get("paperID", title.hashCode());
			tx.success();
		} finally {
			tx.finish();
		}
		return hits;
	}
	/*
	 * list all the informations of a node
	 */
	public void listNodeAttributes(long id) {
		Node node = graphDb.getNodeById(id);
		Iterator<String> keys = node.getPropertyKeys().iterator();

		System.out.println("=========Properties for the node==========");
		while (keys.hasNext()) {
			String key = keys.next();
			System.out.println(key + " : " + node.getProperty(key).toString());
		}

		System.out.println("=========Relationships for node===========");

		Iterator<Relationship> rels = node.getRelationships(Direction.INCOMING)
				.iterator();
		while (rels.hasNext()) {
			Relationship rel = rels.next();
			Node start = rel.getStartNode();

			System.out.println("(Me) <-- " + rel.getType() + " <-- "
					+ start.getId());
		}

		rels = node.getRelationships(Direction.OUTGOING).iterator();
		while (rels.hasNext()) {
			Relationship rel = rels.next();
			Node end = rel.getEndNode();

			System.out.println("(me) --> " + rel.getType() + " --> "
					+ end.getId());
		}
	}

	/*
	 * give a brief summary for the database
	 */
	public void summary(long beginIndex, long endIndex) {
		Iterator<Node> all = graphDb.getAllNodes().iterator();

		Node node = null;
		int paperNumber = 0;
		int authorNumber = 0;
		int allNumber = 0;
		
		while (all.hasNext() && allNumber < beginIndex) {
			all.next();
			allNumber++;
		}
		
		while (all.hasNext() && allNumber < endIndex) {
			node = all.next();
			allNumber++;
			if (node.hasProperty("full name"))
				authorNumber++;
			else if (node.hasProperty("title")) {
				String title = (String) node.getProperty("title");
				if (!title.equals(EXTERNAL))
					paperNumber++;
			}
				
		}


		System.out.println("Totally there're " + allNumber + " nodes.");
		System.out.println("\t" + paperNumber + " papers.");
		System.out.println("\t" + authorNumber + " authors.");
	}

	// for test
	public boolean isBetween2Nodes(Node myNode, String title, String[] names) {
		// my title is cited by titles
		Transaction tx = graphDb.beginTx();
		IndexHits<Node> hits, anotherHits;
		String another = title + ".";
		boolean found = false;
		try {
			hits = papers.get("paperID", title.hashCode());
			anotherHits = papers.get("paperID", another.hashCode());

			if (hits.size() != 0 || anotherHits.size() != 0) {
				/*
				 * if find, compare authors
				 */
				IndexHits<Node> goal = null;
				String[] authors = null;
				if (hits.size() != 0)
					goal = hits;
				else
					goal = anotherHits;

				// if there's one author match, we trait them as the same
				lable: for (Node p : goal) {
					authors = (String[]) p.getProperty("authors");
					for (String name : names)
						for (String author : authors)
							if (name.equals(author)) {
								found = true;
								break lable;
							}
				}
			}

			tx.success();
		} finally {
			tx.finish();
		}
		return found;
	}

	// for test
	public void getCoauthors() {
		long id1 = 49609;
		long id2 = 49604;

		Node a1 = graphDb.getNodeById(id1);
		Node a2 = graphDb.getNodeById(id2);
		String name1 = (String) a1.getProperty("full name");
		String name2 = (String) a2.getProperty("full name");
		System.out.println(name1 + "\n" + name2);
		for (Relationship co : coauthors.query("index", name1 + "_" + name2))
			System.out.println(co.getProperty("title"));

	}

	// for test: used to delete the test citation
	public void deleteCitationBetween2Nodes(long endPaperId, String startTitle) {
		tx = graphDb.beginTx();
		try {
			Node startPaper = papers.get("paperID", startTitle.hashCode())
					.getSingle();
			if (startPaper != null) {
				System.out.println("Start id = " + startPaper.getId());
				Node endPaper = graphDb.getNodeById(endPaperId);
				String endTitle = (String) endPaper.getProperty("title");

				Relationship cite = cites.get("index",
						startTitle + "_" + endTitle).getSingle();
				Relationship cited = citedBys.get("index",
						endTitle + "_" + startTitle).getSingle();
				if (cite == null || cited == null) {
					System.out.println(cite);
					System.out.println(cited);
					System.err.print("Errors 1.");
					System.exit(1);
				} else {
					cites.remove(cite);
					cite.delete();
					citedBys.remove(cited);
					cited.delete();

					Traverser endTraverser = this.getAuthorsTraverser(endPaper);
					Traverser startTraverser = this
							.getAuthorsTraverser(startPaper);
					for (Node endAuthor : endTraverser) {
						String endAuthorName = (String) endAuthor
								.getProperty("full name");

						for (Node startAuthor : startTraverser) {
							String startAuthorName = (String) startAuthor
									.getProperty("full name");

							String value = startAuthorName + "_" + startTitle
									+ "_" + endTitle + "_" + endAuthorName;
							Relationship personCite = personCites.get("index",
									value).getSingle();
							value = endAuthorName + "_" + endTitle + "_"
									+ startTitle + "_" + startAuthorName;
							Relationship personCitedBy = personCitedBys.get(
									"index", value).getSingle();

							if (personCite == null || personCitedBy == null) {
								System.err.print("Errors 2.");
								System.exit(1);
							} else {
								personCites.remove(personCite);
								personCite.delete();
								personCitedBys.remove(personCitedBy);
								personCitedBy.delete();
							}
						}
					}
				}
			} else {
				System.err.print("Errors 3.");
				System.exit(1);
			}
			tx.success();
		} finally {
			tx.finish();
		}
	}

	// create external node
	public void createExternalNode() {
		tx= graphDb.beginTx();
		try {
			Node externalNode= graphDb.createNode();
			externalNode.setProperty("title", EXTERNAL);
			externalNode.setProperty("type", EXTERNAL);
			
			long id= externalNode.getId();
			
			System.out.println(id);
		} finally {
			tx.success();
			tx.finish();
		}
	}
	
	/*
	 * Add new index for papers
	 */
	public void addIndexForPapers() {
		String str = null;
		
		tx = graphDb.beginTx();
		try {
			int order = 1;
			String title = null;
			String base = null;
			for (Node paper : papers.query("paperID", "*")) {
				title = (String) paper.getProperty("title");
				
				Pattern p = Pattern.compile("\\W");// 增加对应的标点
				Matcher m = p.matcher(title);
				base = m.replaceAll(" ");
				p = Pattern.compile(" {2,}");// 去除多余空格
				m = p.matcher(base);
				base = m.replaceAll(" ");
				base = base.toLowerCase();
				
				str = order + " : " + paper.getProperty("title") + "\n";
				System.out.print(str);
				str = "\t\t" + base + "\n";
				System.out.print(str);
				
//				papers.add(paper, "order", order);
//				papers.add(paper, "title", title);
				papers.remove(paper, "base");
				papers.add(paper, "base", base);
				order++;
				
				if (order % 100000 == 0) {
					tx.success();
					tx.finish();
					tx = graphDb.beginTx();
				}
			}
			System.out.println("Order : " + order);
			tx.success();
		} finally {
			tx.finish();
		}
	}
	
	/*
	 * Add new index for authors
	 */
	public void addIndexForAuthors() {
		String str = null;
		tx = graphDb.beginTx();
		try {
			int order = 1;
			String name = null;
			for (Node author : authors.query("authorID", "*")) {
				name = (String) author.getProperty("full name");
				str = order + " : " + name + "\n";
				System.out.print(str);
				
				authors.add(author, "order", order);
				authors.add(author, "name", name);
				order++;
				
				if (order % 100000 == 0) {
					tx.success();
					tx.finish();
					tx = graphDb.beginTx();
				}
			}
			System.out.println("Order : " + order);
			tx.success();
		} finally {
			tx.finish();
		}
	}
	
	/*
	 * add new ID property for the nodes found in CiteSeerX
	 */
	public void addIDProperty() {
		tx = graphDb.beginTx();
		try {
			FileReader fr = new FileReader(new File("outputs/old_new_ID.txt"));
			BufferedReader br = new BufferedReader(fr);
			String line = null;
			long oldId = -1;
			long newId = -1;
			long order = -1;
			Node node = null;
			while (br.ready()) {
				line = br.readLine();
				String[] strs = line.split("\t");
				order = Long.parseLong(strs[0]);
				oldId = Long.parseLong(strs[1]);
				newId = Long.parseLong(strs[2]);
				System.out.println(order + "\t" + oldId + "\t" + newId);
				node = graphDb.getNodeById(oldId);
				node.setProperty("new id", newId);
				if (order % 10000 == 0) {
					tx.success();
					tx.finish();
					tx = graphDb.beginTx();
				}
			}
			tx.success();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			tx.finish();
		}
	}
	
	/*
	 * find homonyme for the authors
	 */
	public void findAuthorHomonyms() {
		Log log = new Log("Author_Homonym.txt");
		String str = null;
		Hashtable<String, Family> ht = new Hashtable<String, Family>();
//		HashMap<String, Family> classment = new HashMap<String, Family>();
		Author author = new Author();
		for (Node node : authors.query("name", "*")) {
			System.out.println(node.getId());
			author.getFromNode(node);
			if (ht.containsKey(author.getLastName().toLowerCase())) {
				Family f = ht.get(author.getLastName().toLowerCase());
				f.addMember(node);
			} else {
				Family nf = new Family(author.getLastName());
//				classment.put(author.lastName.toLowerCase(), nf);
				ht.put(author.getLastName().toLowerCase(), nf);
			}
		}
		
		str = "Totally, there're " + ht.size() + " families.\n";
		System.out.print(str);
		log.write(str);
		
		str = "\nBegin to scan families ==> \n";
		System.out.print(str);
		log.write(str);
		
		Family f = null;
		Node n1, n2 = null;
		Author a1 = new Author();
		Author a2 = new Author();
		Enumeration<Family> families = ht.elements();
		int familyNumber = 0;
		int count = 0;
		int ifmerge = 0;
		while (families.hasMoreElements()) {
			familyNumber++;
			f = families.nextElement();
			str = "The " + familyNumber + " th family : " + f.familyName + "\n";
			log.write(str);
			System.out.print(str);
			for (int i= 0; i < f.members.size(); i++) {
				boolean found = false;
				n1 = f.members.get(i);
				a1.getFromNode(n1);
				for (int j = i+1; j < f.members.size(); j++) {
					n2 = f.members.get(j);
					a2.getFromNode(n2);
					if (a1.equals(a2)) {
						count++;
						found = true;
						str = count + "\t" + n1.getId() + "\t" + a1.getFullName() + "\t" +
								n2.getId() + "\t" + a2.getFullName() + "\n";
						System.out.print(str);
						log.write(str);
					}
				}
				if (found)
					ifmerge++;
			}
		}
		str = "\nIf every duplicate can be merged, " + ifmerge + " author will be deleted.\n";
		System.out.print(str);
		log.write(str);
		log.close();
		System.out.println("Finished!");
	}
	
	private class Family {
		public String familyName;
		ArrayList<Node> members;
		
		public Family(String familyName) {
			this.familyName = familyName;
			this.members = new ArrayList<Node>();
		}
		
		public void addMember(Node author) {
			members.add(author);
		}
	}
	
	/*
	 * find homonyms for the papers
	 */
	public void findPaperHomonyms() {
		Log log = new Log("Paper_Homonym.txt");
		String str = null;
		String title, base = null;
		int count = 0;
		for (Node paper : papers.query("title", "*")) {
			title = (String) paper.getProperty("title");
			
			Pattern p = Pattern.compile("\\W");// 增加对应的标点
			Matcher m = p.matcher(title);
			base = m.replaceAll(" ");
			p = Pattern.compile(" {2,}");// 去除多余空格
			m = p.matcher(base);
			base = m.replaceAll(" ");
			base = base.toLowerCase();
			
			IndexHits<Node> hits = papers.get("base", base);
			System.out.println(paper.getId());
			if (hits.size() > 1) {
				count++;
				str = count + "\t" + paper.getId() + "\t" + title + "\n";
				System.out.print(str);
				log.write(str);
				
				for (Node node :hits) {
					if (node.getId() == paper.getId())
						continue;
					str = "\t==>" + node.getId() + "\t" + (String) node.getProperty("title") + "\n"; 
					System.out.print(str);
					log.write(str);
				}
			} else if (hits.size() == 1) {
				System.out.println("No duplicate for this paper.");
			} else {
				System.err.println("Error");
				System.exit(1);
			}
		}
		str = "Totally there're " + count + " homonyms of papers.\n";
		System.out.print(str);
		log.write(str);
		log.close();
	}
	
	// read database
	public void readDB(long beginIndex, long endIndex) {
		long id = beginIndex;
		Log out = new Log("All_Papers.txt");
		String str = null;
		int count = 0;
		Node node = null;
		while (id <= endIndex) {
			node = graphDb.getNodeById(id);
			if (node.hasProperty("title")) {
				count++;
				str = count + "\t" + id + "\t" + node.getProperty("title") + "\n";
				out.write(str);
				System.out.print(str);
			}
			id++;
		}
		out.close();
	}
	
	public static void main(String[] args) {
		GraphDBUI gdbui = new GraphDBUI();
//		gdbui.readDB(1289321, 1295333);
//		gdbui.createExternalNode();
//		gdbui.addIndexForPapers();
//		gdbui.addIndexForAuthors();
//		gdbui.calculCitedTimesForAllPapers();
//		gdbui.calculateCitaNumAndPubliNumOfAuthor();
//		gdbui.calculHIndex();
//		gdbui.listNodeAttributes(EXTERNALNODEID);

//		gdbui.findAuthorHomonyms();
//		gdbui.findPaperHomonyms();
//		gdbui.summary(1289321, 1369585);
		
//		gdbui.addIDProperty();
		gdbui.shutDown();
	}
}
